#! /usr/bin/env python
#coding=utf-8

import os

from .builder import Builder

class ModuleBuilder(Builder):
	MODULE_GROUPS = ("publicapi", "innerapi_chc", "innerapi_cc", "innerapi_chc_indirect", "innerapi_cc_indirect", "pentry", "private")
	MODULE_GROUP_DESC = ("公共API模块", "跨组件Inner API模块", "跨部件Inner API模块", "跨组件间接依赖模块", "跨部件间接依赖模块", "进程入口模块", "组件内私有模块")

	@staticmethod
	def getModuleGroupDesc(group):
		idx = ModuleBuilder.MODULE_GROUPS.index(group)
		return ModuleBuilder.MODULE_GROUP_DESC[idx]

	def __init__(self):
		pass

	def _getGraphVizColor(self, mod):
		if mod["modGroup"] == "publicapi":
			return "aquamarine"
		if mod["modGroup"] in ("innerapi_chc", "innerapi_chc_indirect"):
			return "green"
		elif mod["modGroup"] in ("innerapi_cc", "innerapi_cc_indirect"):
			return "cornsilk"

		return "gray"

	def _getGraphVizShape(self, mod):
		if mod["modGroup"] in ("publicapi", "pentry"):
			return "box3d"
		if mod["modGroup"] in ("innerapi_chc", "innerapi_cc"):
			return "component"
		if mod["modGroup"] in ("innerapi_chc_indirect", "innerapi_cc_indirect"):
			return "tab"
		return "box"

	def _getModuleOverview(self, mod):
		return "%s,deps %d,total %d,by %d" % (mod["human_size"], len(mod["deps"]), mod["deps_total"], len(mod["dependedBy"]))

	def __getHeaderItems(self, mod):
		cols = 0
		colors = []
		if mod["modGroup"] == "innerapi_chc":
			if mod["chipsetsdk"]:
				colors.append("blue")
				cols = cols + 1
			if mod["platformsdk"]:
				colors.append("red")
				cols = cols + 1
		elif mod["modGroup"] in ("innerapi_cc", "publicapi", "pentry"):
			colors.append(self._getGraphVizColor(mod))
			cols = cols + 1
		return ("".join(['<td SIDES="LRT" COLSPAN="1" FIXEDSIZE="TRUE" HEIGHT="8" WIDTH="14" BORDER="1" BGCOLOR="%s" ALIGN="left"></td>' % color for color in colors]), cols)

	def _getHeaderStr(self, context):
		mod = context.obj

		(items, cols) = self.__getHeaderItems(mod)
		res = '<tr>' + items

		while cols < 2:
			res += "<td></td>"
			cols = cols + 1

		if mod["chipset"]:
			chipset="C"
		else:
			chipset="S"
		res += '<td style="rounded" FIXEDSIZE="TRUE" HEIGHT="10" WIDTH="10" BORDER="1" BGCOLOR="%s" CELLPADDING="0" CELLSPACING="0" ALIGN="left"><font POINT-SIZE="10">%s</font></td>' % (self._getGraphVizColor(mod), chipset)

		res += '</tr>'
		return res

	def __getDetailTd(self, mod):
		detailUrl = "javascript:top.fnShowModuleDetails(%d)" % mod["id"]
		return '<td SIDES="RTB" BORDER="1" ROWSPAN="2" COLSPAN="1" BGCOLOR="%s" href="%s" title="%s"><u><font color="blue">detail</font></u></td>' % (self._getGraphVizColor(mod), detailUrl, detailUrl)

	def __getCommonTd(self, mod, sides, span, text, href=None):
		if span > 2:
			sides += "R"
		if href:
			val = 'href="%s" title="%s"><u><font color="blue">%s</font></u>' % (href, href, text)
		else:
			val = '>%s' % (text)
		return '<td SIDES="%s" BORDER="1" COLSPAN="%d" BGCOLOR="%s" align="center" %s</td>' % (sides, span, self._getGraphVizColor(mod), val)

	# https://graphviz.gitlab.io/doc/info/shapes.html#epsf
	def _getLabel(self, context):
		mod = context.obj

		urlPrefix = self._getUrlPrefix(context)
		args = []
		for k in ("type", "format"):
			args.append("%s=%s" % (k, context.xargs[k]))
		args = "&amp;amp;".join(args)
		fullUrl = os.path.join(urlPrefix, "fields") + "?" + args

		span = 2
		details = self.__getDetailTd(mod)
		if context.xargs and "format" in context.xargs and context.xargs["format"] == "png":
			span = 3
			details = ""
			fullUrl = None

		# SIDES="RB" SIDES="LB"
		return '<<table BORDER="0" CELLBORDER="0" CELLSPACING="0" ALIGN="center">%s' \
				'<tr>%s' \
				'%s</tr>' \
				'<tr>%s</tr></table>>' % \
				(self._getHeaderStr(context), self.__getCommonTd(mod, "LT", span, mod["name"], fullUrl), details, self.__getCommonTd(mod, "LB", span, self._getModuleOverview(mod)))

	def _getUrlPrefix(self, context):
		product = context.xargs["_cur_product"]
		return os.path.join(product.getUrlPrefix(), "modules", str(context.obj["id"]))

	def getGraphVizInfo(self, context):
		labelStr = self._getLabel(context)
		return '"m%d" [label=%s];\n' % (context.obj["id"], labelStr)

	def _getDepStyle(self, dep):
		if dep["chipsetsdk"]:
			return 'color=blue'
		if dep["platformsdk"]:
			return 'color=red'
		if not dep["external"]:
			return 'style=dashed, color=grey, arrowhead="vee"'
		return ''

	def getDepGraphVizInfo(self, context):
		dep = context.obj
		if context.xargs and "format" in context.xargs and context.xargs["format"] == "png":
			return "m%d -> m%d [label=%s, %s fontcolor=blue];\n" % (dep["caller"]["id"], dep["callee"]["id"], str(dep["calls"]), self._getDepStyle(dep))
		linkStr = "javascript:top.fnEvtShowSymbolsForDeps(%d)" % dep["id"]
		labelStr = "<<u>%s</u>>" % str(dep["calls"]) # underline

		return "m%d -> m%d [label=%s, %s href=\"%s\", title=\"%s\", fontcolor=blue];\n" % (dep["caller"]["id"], dep["callee"]["id"], labelStr, self._getDepStyle(dep), linkStr, linkStr)

	def _generateAllDepsGraph(self, context):
		(children, deps) = context.obj.traverse_deps_tree_by_depth(None, None)
		vizStr = self.getGraphVizInfo(context)
		context.f.write(vizStr)

		#ipc_core = mod._mgr.get_module_by_path("system/lib/chipset-pub-sdk/libipc_core.z.so")

		for m in children:
			if context.graphviz.isModuleIgnored(m):
				continue
			#if self.__ignore_module(mod, ipc_core, m):
			#	continue
			context.setObj(m)
			vizStr = self.getGraphVizInfo(context)
			context.f.write(vizStr)

		for dep in deps:
			if context.graphviz.isModuleIgnored(dep["callee"]):
				continue
			#if self.__ignore_module(mod, ipc_core, dep["callee"]):
			#	continue
			context.setObj(dep)
			vizStr = self.getDepGraphVizInfo(context)
			context.f.write(vizStr)

	def _generateAllDependedByGraph(self, context):
		(parents, deps) = mod.traverse_dependedBy_tree_by_depth(None, None)
		vizStr = self.getGraphVizInfo(context)
		context.f.write(vizStr)

		for m in parents:
			context.setObj(m)
			vizStr = self.getGraphVizInfo(context)
			context.f.write(vizStr)

		for dep in deps:
			context.setObj(dep)
			vizStr = self.getDepGraphVizInfo(context)
			context.f.write(vizStr)

	def _generateContextGraph(self, context):
		mod = context.obj
		dependedBy = mod["dependedBy"]
		if "__category" in context.xargs:
			if context.xargs["__category"] == "chipsetsdk":
				dependedBy = mod.getChipsetSdkDependedBy()
			elif context.xargs["__category"] == "platformsdk":
				dependedBy = mod.getPlatformSdkDependedBy()
			elif context.xargs["__category"] == "dependedBy_external":
				dependedBy = [m for m in mod["dependedBy"] if m["external"]]
		for dep in dependedBy:
			m = dep["caller"]
			context.setObj(m)
			vizStr = self.getGraphVizInfo(context)
			context.f.write(vizStr)

		context.setObj(mod)
		vizStr = self.getGraphVizInfo(context)
		context.f.write(vizStr)

		for dep in mod["deps"]:
			context.setObj(dep["callee"])
			vizStr = self.getGraphVizInfo(context)
			context.f.write(vizStr)

		for dep in dependedBy + mod["deps"]:
			context.setObj(dep)
			vizStr = self.getDepGraphVizInfo(context)
			context.f.write(vizStr)

	@staticmethod
	def graph_writer(dep, depth, cookie):
		f = cookie[0]
		maxDepth = cookie[1]
		stop = False
		minChild = 0
		if len(cookie) > 2:
			minChild = cookie[2]

		if minChild > 0 and len(dep["callee"]["deps"]) < minChild:
			# not enough child, just ignore
			return True

		if maxDepth > 0 and depth + 1 > maxDepth:
			# Ignore next depth children
			stop = True

		callerName = "\"" + dep["caller"].getDetailName() + "\""
		calleeName = "\"" + dep["callee"].getDetailName() + "\""

		f.write(callerName + " -> " + calleeName)
		#if child not in parent["deps"]:
		f.write("[label=\"%s\"]" % (dep["calls"]))
		f.write(";\n")

		if stop and len(dep["callee"]["deps"]) > 0:
			f.write(calleeName + " [ style = filled ];\n")

		return stop

	def _generateGraphByDepth(self, context):
		context.obj.traverse_deps_tree_by_depth(ModuleBuilder.graph_writer, (context.f, 15))

	def generateObjectGraph(self, context):
		context.f.write("node [shape=plaintext]\n")
		if context.xargs["type"] not in ("deps_total", "dependedBy_total", "depth"):
			context.f.write("rankdir=\"LR\"\n")
		if context.xargs["type"] == "depth":
			self._generateGraphByDepth(context)
		elif context.xargs["type"] == "deps_total":
			self._generateAllDepsGraph(context)
		elif context.xargs["type"] == "dependedBy_total":
			self._generateAllDependedByGraph(context)
		else:
			self._generateContextGraph(context)
